/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER)

#include <trace_log.h>
#include "umc_avs_dec_decompressor.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_slice_header.h"
#include "umc_avs_dec_bit_stream.h"
#include "umc_avs_mb_info.h"
#include "umc_avs_dec_tables.h"

namespace UMC
{

void AVSDecompressor::DecompressBMacroBlocksRow(void)
{
    MbMax = m_decCtx.MbIndex + m_decCtx.MbWidth;

    // we can use MbIndex either from decoding and reconstructing contexts
    MbIndex = m_decCtx.MbIndex;

    PrepareNeighbours(&m_decCtx);
    PrepareDecoding();
    PrepareReconstruction();

    do
    {
        if (0 == MbIndex)
        {
            WRITE_TRACE_LOG(_dec_tst, "Frame num is  ", m_decCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_vec_tst, "Frame num is  ", m_decCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_vec_final_tst, "Frame num is  ", m_decCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_rec_tst, "Frame num is  ", m_decCtx.m_pPicHeader->picture_distance - 1);
        }
        WRITE_TRACE_LOG_MB_TITLE(_dec_tst, MbIndex, m_decCtx.MbWidth, m_decCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_rec_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_recCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_dir_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_final_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);

        ResetMacroblock();

        // try to get number of skipped macroblocks
        if (0 == m_decCtx.SkipMbCount)
        {
            if (1 == m_decCtx.m_pPicHeader->skip_mode_flag)
            {
                m_decCtx.SkipMbCount = GetUE(&m_decCtx.m_stream);
                WRITE_TRACE_LOG(_dec_tst, "skip mb count", m_decCtx.SkipMbCount);
            }
        }
        // the previous macroblock was skipped or skip_mb_run is 0,
        // so this one is decoded
        else
        {
            m_decCtx.SkipMbCount -= 1;
        }

        // decode next macroblock
        if (0 >= m_decCtx.SkipMbCount)
        {
            // set coeffs buffers
            m_pWriteCoeffs = m_decCtx.m_pCoeffs;
            m_pReadCoeffs = m_recCtx.m_pCoeffs;

            DecodeBMacroBlockType();

            // call appropriate routines
            switch (m_pMbInfo->MbType)
            {
            case B_Skip:
                DecodeSkipBMacroBlock();
                ReconstructBMacroBlock();
                break;

            case I_8x8:
                DecodeIMacroBlock();
                ReconstructIMacroBlock();
                break;

            default:
                DecodeBMacroBlock();
                ReconstructBMacroBlock();
                break;
            };
        }
        // next macroblock is skipped
        else
        {
            DecodeSkipBMacroBlock();
            ReconstructBMacroBlock();
        }

        MbIndex += 1;
        AdvanceNeighbours();
        AdvanceDecoding();
        AdvanceReconstruction();

    } while (MbIndex < MbMax);

    // update context
    FinalizeDecoding();
    FinalizeReconstruction();

} // void AVSDecompressor::DecompressBMacroBlocksRow(void)

void AVSDecompressor::DecodeBMacroBlocksRow(void)
{
    MbMax = m_decCtx.MbIndex + m_decCtx.MbWidth;

    // we can use MbIndex from decoding context only
    MbIndex = m_decCtx.MbIndex;

    // set coeffs buffers
    m_pWriteCoeffs = m_decCtx.m_pCoeffs;

    PrepareNeighbours(&m_decCtx);
    PrepareDecoding(true);

    do
    {
        WRITE_TRACE_LOG_MB_TITLE(_dec_tst, MbIndex, m_decCtx.MbWidth, m_decCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_dir_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_vec_final_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);

        ResetMacroblock();

        // try to get number of skipped macroblocks
        if (0 == m_decCtx.SkipMbCount)
        {
            if (1 == m_decCtx.m_pPicHeader->skip_mode_flag)
            {
                m_decCtx.SkipMbCount = GetUE(&m_decCtx.m_stream);
                WRITE_TRACE_LOG(_dec_tst, "skip mb count", m_decCtx.SkipMbCount);
            }
        }
        // the previous macroblock was skipped or skip_mb_run is 0,
        // so this one is decoded
        else
        {
            m_decCtx.SkipMbCount -= 1;
        }

        // decode next macroblock
        if (0 >= m_decCtx.SkipMbCount)
        {
            DecodeBMacroBlockType();

            // call appropriate routines
            switch (m_pMbInfo->MbType)
            {
            case B_Skip:
                DecodeSkipBMacroBlock();
                break;

            case I_8x8:
                DecodeIMacroBlock();
                break;

            default:
                DecodeBMacroBlock();
                break;
            };
        }
        // next macroblock is skipped
        else
        {
            DecodeSkipBMacroBlock();
        }

        // update area affected by macroblock motion vectors
        UpdateAffectedArea();

        MbIndex += 1;
        AdvanceNeighbours();
        AdvanceDecoding();

    } while (MbIndex < MbMax);

    // update context
    FinalizeDecoding();

} // void AVSDecompressor::DecodeBMacroBlocksRow(void)

void AVSDecompressor::DecodeBMacroBlockType(void)
{
    Ipp32u MbTypeIndex;

    // get entopy element
    MbTypeIndex = GetUE(&(m_decCtx.m_stream));
    WRITE_TRACE_LOG(_dec_tst, "mb type", MbTypeIndex);

    if (m_decCtx.m_pPicHeader->skip_mode_flag)
        MbTypeIndex += 1;

    if (24 <= MbTypeIndex)
    {
        // actually, it is an INTRA macroblock

        // error protection
        if (63 + 24 < MbTypeIndex)
            throw (int) 0;

        m_pMbInfo->MbType = I_8x8;
        m_pMbInfo->MbCBP = CBPOfMacroblock[AVS_INTRA][MbTypeIndex - 24];
    }
    else
    {
        // set default parameters for P macroblock
        m_pMbInfo->MbType = TypesOfBMacroblocks[MbTypeIndex].MbType;
        m_pMbInfo->MvNum = (Ipp16u) TypesOfBMacroblocks[MbTypeIndex].MvNum;
        m_pMbInfo->MbCBP = AVS_CBP_UNKNOWN;
        memcpy(m_pMbInfo->predType,
               TypesOfBMacroblocks[MbTypeIndex].predType,
               sizeof(m_pMbInfo->predType));
        m_pMbInfo->divType = TypesOfBMacroblocks[MbTypeIndex].divType;
    }

    m_pMbInfo->QP = m_decCtx.PreviousQP;

} // void AVSDecompressor::DecodeBMacroBlockType(void)

void AVSDecompressor::DecodeBMacroBlock(void)
{
    Ipp32u i;

    if (B_Direct_16x16 != m_pMbInfo->MbType)
    {
        // decode block types
        if (B_8x8 == m_pMbInfo->MbType)
        {
            for (i = 0; i < 4; i += 1)
            {
                Ipp32u predType;

                // decode block type
                predType = (Ipp8u) GetBits(&m_decCtx.m_stream, 2);
                WRITE_TRACE_LOG(_dec_tst, "mb part type", predType);

                if (PredDirect != predType)
                    m_pMbInfo->MvNum = (Ipp16u) (m_pMbInfo->MvNum + 1);
                // correct the 'symmetrical' block prediction type,
                // because our consts differ slightly from the standard's consts.
                if (3 == predType)
                    predType = PredSym;

                m_pMbInfo->predType[i] = (Ipp8u) predType;
            }
        }

        // decode reference indecies
        if ((0 == m_decCtx.m_pPicHeader->picture_reference_flag) &&
            (0 == m_decCtx.m_pPicHeader->picture_structure))
        {
            // field structure - indecies are 1 bit wide
            for (i = 0; i < m_pMbInfo->MvNum; i += 1)
            {
                m_refIdx[i] = (Ipp8u) GetBit(&m_decCtx.m_stream);
                WRITE_TRACE_LOG(_dec_tst, "mb reference index", m_refIdx[i]);
            }

            // clone reference indices
            SetRefIndiciesBSlice();
        }

        // decode motion vectors
        for (i = 0; i < m_pMbInfo->MvNum; i += 1)
        {
            mv_diff[i].vector.x = (Ipp16s) GetSE(&m_decCtx.m_stream);
            WRITE_TRACE_LOG(_dec_tst, "mv diff x", mv_diff[i].vector.x);
            mv_diff[i].vector.y = (Ipp16s) GetSE(&m_decCtx.m_stream);
            WRITE_TRACE_LOG(_dec_tst, "mv diff y", mv_diff[i].vector.y);
        }

        // reconstruct motion vectors
        ReconstructMotionVectorsBSlice();
    }
    else
    {
        // reconstruct direct motion vectors
        ReconstructMotionVectorsBSliceDirect();
    }

    // decode weighting prediction flag
    if (m_decCtx.m_pSlcHeader->mb_weighting_flag)
    {
        m_pMbInfo->weighting_prediction = (Ipp8u) GetBit(&m_decCtx.m_stream);
        WRITE_TRACE_LOG(_dec_tst, "weighting prediction", m_pMbInfo->weighting_prediction);
    }
    else
    {
        // if mb_weighting flag is zero,
        // macroblock weighting prediction flag is equal to slice_weighting_flag
        m_pMbInfo->weighting_prediction = (Ipp8u) m_decCtx.m_pSlcHeader->slice_weighting_flag;
    }

    if (AVS_CBP_UNKNOWN == m_pMbInfo->MbCBP)
    {
        Ipp32u uTemp;
        Ipp32s mb_qp_delta = 0;

        // decode coded block pattern
        uTemp = GetUE(&m_decCtx.m_stream);
        WRITE_TRACE_LOG(_dec_tst, "CBP code", uTemp);
        // error handling
        if (63 < uTemp)
            throw (int) 0;
        m_pMbInfo->MbCBP = CBPOfMacroblock[AVS_INTER][uTemp];
        if (AVS_CHROMA_422_FORMAT == m_decCtx.m_pSeqHeader->chroma_format)
        {
            uTemp = GetUE(&m_decCtx.m_stream);
            WRITE_TRACE_LOG(_dec_tst, "CBP code +", uTemp);
            m_pMbInfo->MbCBP |= uTemp << 6;
        }

        // decode quantization parameter
        if ((m_pMbInfo->MbCBP) &&
            (0 == m_decCtx.FixedQP))
        {
            mb_qp_delta = GetSE(&m_decCtx.m_stream);
            WRITE_TRACE_LOG(_dec_tst, "mb qp delta", mb_qp_delta);
            if (63 < mb_qp_delta + 32)
                throw (int) 0;
            m_pMbInfo->QP += mb_qp_delta;
            m_decCtx.PreviousQP += mb_qp_delta;
        }
    }

    // decode blocks coefficients
    {
        Ipp32u uBlockMask = 1;

        // decode luminance blocks
        for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                Ipp32s iNumCoeffs;

                ippiDecodeLumaBlockInter_AVS_1u16s((Ipp32u **) &m_decCtx.m_stream.src,
                                                   &m_decCtx.m_stream.offset,
                                                   &iNumCoeffs,
                                                   m_pWriteCoeffs,
                                                   m_decCtx.ScanType);
                m_pMbInfo->NumCoeffs[i] = (Ipp8u) iNumCoeffs;
                m_pWriteCoeffs += 64;
            }
        }

        // decode chrominance blocks
        for (; i < m_decCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                Ipp32s iNumCoeffs;

                ippiDecodeChromaBlock_AVS_1u16s((Ipp32u **) &m_decCtx.m_stream.src,
                                                &m_decCtx.m_stream.offset,
                                                &iNumCoeffs,
                                                m_pWriteCoeffs,
                                                m_decCtx.ScanType);
                m_pMbInfo->NumCoeffs[i] = (Ipp8u) iNumCoeffs;
                m_pWriteCoeffs += 64;
            }
        }
    }

} // void AVSDecompressor::DecodeBMacroBlock(void)

void AVSDecompressor::DecodeSkipBMacroBlock(void)
{
    // correct MB type & parameters
    m_pMbInfo->MbType = B_Skip;
    m_pMbInfo->MbCBP = 0;
    memset(m_pMbInfo->predType, PredBiDir, sizeof(m_pMbInfo->predType));
    m_pMbInfo->QP = m_decCtx.PreviousQP;
    m_pMbInfo->divType = Div_16x16;

    // decode direct motion vectors
    ReconstructMotionVectorsBSliceDirect();

} // void AVSDecompressor::DecodeSkipBMacroBlock(void)

void AVSDecompressor::SetRefIndiciesBSlice(void)
{
    eAVSPredType predType;
    Ipp32u dir;
    Ipp32s iRefNum = 0;

    // cycle on directions
    predType = PredForward;
    for (dir = AVS_FORWARD; dir <= AVS_BACKWARD; dir += 1)
    {
        // clone reference indecies
        switch (m_pMbInfo->divType)
        {
            // all block have the same reference index
        case Div_16x16:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_pMbInfo->refIdx[dir][0] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][1] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][2] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][3] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            break;

            // top blocks have index 0,
            // bottom blocks have index 1
        case Div_16x8:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_pMbInfo->refIdx[dir][0] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][1] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[2] & predType)
            {
                m_pMbInfo->refIdx[dir][2] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][3] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            break;

            // left blocks have index 0,
            // right blocks have index 1
        case Div_8x16:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_pMbInfo->refIdx[dir][0] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][2] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[1] & predType)
            {
                m_pMbInfo->refIdx[dir][1] = (Ipp8u) m_refIdx[iRefNum];
                m_pMbInfo->refIdx[dir][3] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            break;

            // every block has its own reference index
        default:
            if (m_pMbInfo->predType[0] & predType)
            {
                m_pMbInfo->refIdx[dir][0] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[1] & predType)
            {
                m_pMbInfo->refIdx[dir][1] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[2] & predType)
            {
                m_pMbInfo->refIdx[dir][2] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            if (m_pMbInfo->predType[3] & predType)
            {
                m_pMbInfo->refIdx[dir][3] = (Ipp8u) m_refIdx[iRefNum];
                iRefNum += 1;
            }
            break;
        }

        // set other direction
        predType = PredBackward;
    }

} // void AVSDecompressor::SetRefIndiciesBSlice(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER)
